package org.yanse.structures.stack.calculator;

import java.util.Stack;

/**
    1. 遍历字符串
    2. 如果是数字入数栈
        2.1 如果是连续的数字
            2.1.1 数栈出栈*10+当前数字结果入数栈
    3. 如果是)计算到(处计算结果入数栈
    4. 如果是符号判断符号优先级
        4.1 如果优先级低于符号栈顶
            4.1.1 符号出栈直到优先级不低于符号栈顶
        4.2 入符号栈
    5. 如果是其他字符抛异常
    6. 特殊场景
        6.1 第一个元素或(字符后一位字符是+或-
            6.1.1 0入数栈
    7. 根据数栈和符号栈计算结果入数栈直到符号栈为空
*/
public class Comprehensive {
    private static final Stack<Integer> numStack = new Stack<>();
    private static final Stack<Character> symbolStack = new Stack<>();

    public static void main(String[] args) {
        System.out.println(calc("1+22"));
    }

    public static int calc(String str) {
        if (str == null || str.length() == 0) {
            return 0;
        }

        if (!numStack.isEmpty()) {
            numStack.clear();
        }

        if (!symbolStack.isEmpty()) {
            symbolStack.clear();
        }

        char[] chars = str.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];
            if (isErrorChar(c)) {
                throw new IllegalArgumentException(String.valueOf(c));
            }
            if (i == 0 || chars[i - 1] == '(') {
                if (c == '+' || c == '-') {
                    numStack.add(0);
                    symbolStack.add(c);
                    continue;
                }
            }
            if (c == ')') {
                while (!symbolStack.isEmpty() && symbolStack.peek() != '(') {
                    calcOnce();
                }
                // 此时栈顶为( 如果不是数据异常
                symbolStack.pop();
                continue;
            }
            if (isNum(c)) {
                int num = Integer.parseInt(String.valueOf(c));
                if (i > 0 && isNum(chars[i - 1])) {
                    num += 10 * numStack.pop();
                }
                numStack.add(num);
                continue;
            }
            if (isSymbol(c)) {
                int currentPriority = priority(c);
                while (currentPriority <= getTopSymbolPriority()) {
                    calcOnce();
                }
            }
            symbolStack.add(c);
        }
        calcAll();
        if (!numStack.isEmpty()) {
            return numStack.pop();
        }

        return 0;
    }

    private static int getTopSymbolPriority() {
        if (!symbolStack.isEmpty()) {
            return priority(symbolStack.peek());
        }
        return -1;
    }

    private static void calcAll() {
        while (!symbolStack.isEmpty() && !numStack.isEmpty()) {
            calcOnce();
        }
    }

    private static void calcOnce() {
        int current = numStack.pop();
        int next = numStack.pop();
        char symbol = symbolStack.pop();
        int result = 0;
        switch (symbol) {
            case '+' :
                result = next + current;
                break;
            case '-' :
                result = next - current;
                break;
            case '*' :
                result = next * current;
                break;
            case '/' :
                if (current == 0) {
                    throw new ArithmeticException(String.valueOf(current));
                }
                result = next / current;
                break;
        }
        numStack.add(result);
    }

    private static boolean isNum(Character num) {
        return num >= '0' && num <= '9';
    }

    private static boolean isSymbol(Character symbol) {
        return symbol == '+' || symbol == '-' || symbol == '*' || symbol == '/';
    }

    private static boolean isErrorChar(Character errorChar) {
        return !isNum(errorChar) && !isSymbol(errorChar) && errorChar != '(' &&
               errorChar != ')';
    }

    private static int priority(Character symbol) {
        if (symbol == '+' || symbol == '-') {
            return 0;
        }
        if (symbol == '*' || symbol == '/') {
            return 1;
        }
        return -1;
    }
}
